Quoting
Need the canonical lists of coverage selection tokens or pet age enum values? See Accepted Request Values for the authoritative reference.
Breeds List
Retrieve the list of supported breeds to validate breedId values before submitting a quote. This list is required to translate user species + breed selections into the numeric breedId sent in the pets array.
Reference
Endpoint documentation: Breeds Endpoint
Sample Response
[
{ "id": 0, "name": "string", "species": "Dog" }
]
Usage Guidance
- Cache results briefly (5–15 min) to reduce redundant calls.
- Filter client-side by species as soon as the user selects Dog/Cat.
- Validate that a chosen
breedIdexists in the cached list prior to POSTing a quote. - Graceful failure: disable breed dropdown + show retry if fetch fails.
Front-End Example (Populate Breed Dropdown)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
async function loadBreeds() {
try {
const res = await fetch('/breeds');
if (!res.ok) throw new Error('Failed to load breeds');
const breeds = await res.json();
const species = document.getElementById('speciesSelect').value;
const filtered = species ? breeds.filter(b => b.species === species) : breeds;
const select = document.getElementById('breedSelect');
select.innerHTML = '<option value="">Select breed...</option>';
filtered.forEach(b => {
const opt = document.createElement('option');
opt.value = b.id;
opt.textContent = b.name;
select.appendChild(opt);
});
} catch (e) {
console.error(e);
alert('Unable to load breeds.');
}
}
document.getElementById('speciesSelect').addEventListener('change', loadBreeds);
window.addEventListener('DOMContentLoaded', loadBreeds);
Backend Example (Program.cs)
1
2
3
4
5
6
7
8
9
public record Breed(int Id, string Name, string Species);
app.MapGet("/breeds", async () =>
{
var forward = new HttpRequestMessage(HttpMethod.Get, "https://[embrace-test-endpoint]/v2/breeds");
forward.Headers.Add("epi-apim-subscription-key", embraceApiKey);
var resp = await httpClient.SendAsync(forward);
var payload = await resp.Content.ReadAsStringAsync();
return Results.Content(payload, "application/json", resp.StatusCode);
});
Request a Quote
Use this guide to perform the initial quote request common to both the Quote Engine Redirect flow and the Stripe Purchase flow. After obtaining a quoteId (and optionally a quoteLinkUrl), continue to:
- Redirect flow: Display Quote Details & Redirect
- Stripe flow: Checkout Request
Make sure to review the full endpoint schema on the Quote API page: Quote Endpoint
Note
This page shows a representative sample of the quote request and response. Refer to the full schema for optional and conditionally required fields.Steps
- Retrieve a
breedId(see Breeds List). - Collect customer + pet inputs (contact info, pets array, desired coverage selections).
- (Optional) Add one or more
rateShopCoverageSelectionsentries (see Rate Shopping Overview) if you want multiple coverage premium scenarios returned in a single call. - POST the JSON payload to
/v2/quotes/fullquotevia your backend proxy (/submit-quote). - Persist
quoteId(andpremiumSummary.quoteLinkUrlif using the redirect flow). - Continue to either redirect flow (display + redirect) or Stripe checkout flow.
Front-End Example (index.js)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
document.getElementById('quoteForm').addEventListener('submit', function(e) {
e.preventDefault();
// Example request body (adapt as needed to your UI values)
const data = {
analytics: {
medium: 'string',
term: 'string',
content: 'string',
campaign: 'summer-sale',
source: 'string'
},
pets: [
{
name: 'Charlie',
breedId: 452,
gender: 'Male',
age: 'One'
}
],
contact: {
ratingZipCode: '12345',
email: 'example@example.com',
phoneNumber: '555-555-5555',
brand: 'embrace'
},
rateShopCoverageSelections: [
{
reimbursementPercentage: '70',
annualWellnessReward: '450',
annualDeductible: '500',
annualReimbursementLimit: 'Unlimited'
}
],
enableEmbraceRemarketing: false
};
fetch('/submit-quote', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
const quoteId = data.quoteId;
if (quoteId) {
const url = new URL(window.location);
url.searchParams.set('quoteId', quoteId);
window.history.replaceState(null, '', url);
// For Redirect Flow, store premiumSummary.quoteLinkUrl
// For Stripe Flow, proceed later to Checkout endpoint using this quoteId
} else {
console.error('quoteId not found in response:', data);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error submitting data.');
});
});
Quote Response Model (Models.cs)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
using System.Text.Json.Serialization;
public record QuoteResponse(
string? QuoteId,
DateTime? PolicyStartDate,
Contact? Contact,
PremiumSummary? PremiumSummary,
List<FullQuotePet>? FullQuotePets,
List<RateShopQuoteResult>? RateShopQuoteResults
);
public record Contact(
string? Id,
string? RatingZipCode,
string? Email,
string? PhoneNumber,
string? Brand,
bool? IsMilitary,
string? SenderId,
bool? OptInTextMessage
);
public record PremiumSummary(
string? PaymentFrequency,
decimal? InsurancePremium,
decimal? TotalDiscounts,
decimal? WellnessPremium,
decimal? StateTaxes,
decimal? MonthlyBillingFee,
decimal? MonthlyPremium,
decimal? PaymentDueRecurring,
decimal? OneTimeEnrollmentFee,
decimal? PaymentDueToday,
string? QuoteLinkUrl,
bool? EligibleForMilitaryDiscount
);
public record FullQuotePet(
string? Name,
int? BreedId,
string? Gender,
string? Age,
string? Id,
string? BreedName,
string? Species,
Coverage? Coverage
);
public record Coverage(
string? AnnualDeductible,
string? AnnualDeductibleDisplay,
string? AnnualReimbursementLimit,
string? AnnualReimbursementLimitDisplay,
string? AnnualWellnessReward,
string? AnnualWellnessRewardDisplay,
bool? ExamFeeCoverage,
decimal? ExamFeeCoveragePremium,
bool? DrugDentalCoverage,
decimal? DrugDentalCoveragePremium,
decimal? IllnessPremium,
string? InsuranceType,
CoverageOptions? Options,
string? ReimbursementPercentage,
string? ReimbursementPercentageDisplay,
decimal? TotalPremium,
decimal? WellnessPremium,
bool? EligibleForWellness,
string? ProductName
);
public record CoverageOptions(
List<SelectableOption>? AnnualDeductibles,
List<SelectableOption>? AnnualReimbursements,
List<SelectableOption>? AnnualWellnessRewards,
List<SelectableOption>? ReimbursementPercentages
);
public record SelectableOption(
bool? IsMostPopular,
string? Display,
bool? IsSelected,
string? AnnualDeductible,
string? AnnualReimbursementLimit,
string? AnnualWellnessReward,
string? ReimbursementPercentage
);
public record RateShopQuoteResult(
CoverageSelection? CoverageSelection,
PremiumSummary? PremiumSummary,
List<PetCoverage>? PetCoverages
);
public record CoverageSelection(
string? ReimbursementPercentage,
string? AnnualWellnessReward,
string? AnnualDeductible,
string? AnnualReimbursementLimit,
bool? ExamFeeCoverage,
bool? DrugDentalCoverage,
string? ProductName
);
public record PetCoverage(
string? AnnualDeductible,
string? AnnualDeductibleDisplay,
string? AnnualReimbursementLimit,
string? AnnualReimbursementLimitDisplay,
string? AnnualWellnessReward,
string? AnnualWellnessRewardDisplay,
bool? ExamFeeCoverage,
decimal? ExamFeeCoveragePremium,
bool? DrugDentalCoverage,
decimal? DrugDentalCoveragePremium,
decimal? IllnessPremium,
string? InsuranceType,
CoverageOptions? Options,
string? ReimbursementPercentage,
string? ReimbursementPercentageDisplay,
decimal? TotalPremium,
decimal? WellnessPremium,
bool? EligibleForWellness,
string? ProductName,
string? PetId,
string? PetName
);
Backend Example (Program.cs)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Program.cs (ASP.NET Core minimal API - .NET 8+)
// Assumes Models.cs classes above are in the same project / namespace.
using System.Text;
using System.Text.Json;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var embraceApiKey = Environment.GetEnvironmentVariable("EMBRACE_API_KEY") ?? "YOUR_EMBRACE_API_KEY";
var httpClient = new HttpClient();
app.MapPost("/submit-quote", async (HttpRequest request) =>
{
using var reader = new StreamReader(request.Body, Encoding.UTF8);
var rawBody = await reader.ReadToEndAsync();
if (string.IsNullOrWhiteSpace(rawBody))
return Results.BadRequest(new { error = "Request body is empty" });
var upstream = new HttpRequestMessage(HttpMethod.Post, "https://[embrace-test-endpoint]/v2/quotes/fullquote")
{
Content = new StringContent(rawBody, Encoding.UTF8, "application/json")
};
upstream.Headers.Add("Cache-Control", "no-cache");
upstream.Headers.Add("epi-apim-subscription-key", embraceApiKey);
var upstreamResponse = await httpClient.SendAsync(upstream);
var responseJson = await upstreamResponse.Content.ReadAsStringAsync();
var model = JsonSerializer.Deserialize<QuoteResponse>(responseJson, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
return model is not null
? Results.Json(model, statusCode: (int)upstreamResponse.StatusCode)
: Results.Content(responseJson, "application/json", upstreamResponse.StatusCode);
});
app.Run();
Updating an Existing Quote
A quote is updated by sending a SECOND POST to the same /v2/quotes/fullquote endpoint with the ENTIRE desired state of the quote (all pets, updated coverage selections, and any new pets). The platform treats each request as a full replacement of prior pets & coverages for the email + ratingZipCode pair.
For lifecycle behavior and how prior submissions are replaced, see Quote Retention & Reuse.
Key principles:
- Full Replacement: Omit a pet and it is removed. Include a modified pet or coverage selection and it is updated. Add a new pet object to append it.
- Idempotent for Unchanged Data: Re-sending the identical payload re-computes pricing safely without duplication.
- No Partial Patch: Do NOT send only the changed pet; always send the entire
petsarray. - Quote Identifier: The
quoteIdyou previously received will remain the same as long as the sameemailandratingZipCodewere used.
Example 1: Initial Quote (Single Pet)
{
"analytics": {
"medium": "string",
"term": "string",
"content": "string",
"campaign": "summer-sale",
"source": "string"
},
"contact": {
"ratingZipCode": "44114",
"email": "owner@example.com",
"phoneNumber": "555-123-1234",
"brand": "embrace",
"billingInformation": { "paymentFrequency": "Monthly" }
},
"pets": [
{ "name": "Riley", "breedId": 452, "gender": "Female", "age": "Two" }
],
"rateShopCoverageSelections": [
{
"reimbursementPercentage": "Eighty",
"annualWellnessReward": "450",
"annualDeductible": "500",
"annualReimbursementLimit": "Unlimited",
"examFeeCoverage": true,
"drugDentalCoverage": false
}
]
}
Example 2: Update – Add Second Pet & Change Coverage
Added a second pet, switched reimbursement to Ninety, removed wellness, and applied the new coverage selection to both pets via selectedCoverage.
{
"analytics": {
"medium": "string",
"term": "string",
"content": "string",
"campaign": "summer-sale",
"source": "string"
},
"contact": {
"ratingZipCode": "44114",
"email": "owner@example.com",
"phoneNumber": "555-123-1234",
"brand": "embrace",
"billingInformation": { "paymentFrequency": "Monthly" }
},
"pets": [
{
"name": "Riley",
"breedId": 452,
"gender": "Female",
"age": "Two",
"selectedCoverage": {
"reimbursementPercentage": "Ninety",
"annualWellnessReward": "None",
"annualDeductible": "500",
"annualReimbursementLimit": "Unlimited",
"examFeeCoverage": true,
"drugDentalCoverage": false
}
},
{
"name": "Shadow",
"breedId": 318,
"gender": "Male",
"age": "Nine",
"selectedCoverage": {
"reimbursementPercentage": "Ninety",
"annualWellnessReward": "None",
"annualDeductible": "500",
"annualReimbursementLimit": "Unlimited",
"examFeeCoverage": true,
"drugDentalCoverage": false
}
}
]
}
Example 3: Remove a Pet
To remove the second pet later, exclude it while retaining the coverage applied to remaining pets:
{
"analytics": {
"medium": "string",
"term": "string",
"content": "string",
"campaign": "summer-sale",
"source": "string"
},
"contact": {
"ratingZipCode": "44114",
"email": "owner@example.com",
"phoneNumber": "555-123-1234",
"brand": "embrace",
"billingInformation": { "paymentFrequency": "Monthly" }
},
"pets": [
{
"name": "Riley",
"breedId": 452,
"gender": "Female",
"age": "Two",
"selectedCoverage": {
"reimbursementPercentage": "Ninety",
"annualWellnessReward": "None",
"annualDeductible": "500",
"annualReimbursementLimit": "Unlimited",
"examFeeCoverage": true,
"drugDentalCoverage": false
}
}
]
}
When to Re-Quote vs Reuse
Trigger an update POST when:
- A pet is added, removed, or changed (age, breedId, gender).
- Desired coverage selection changes (reimbursement %, deductible, limit, wellness, flags).
- Payment frequency mode changes (Monthly ↔ Yearly).
- You need additional
rateShopCoverageSelectionsnot previously rated.
Avoid unnecessary calls if the user only toggles between already-returned combinations for a given rate shop result.
Common Pitfalls
- Sending only the new pet (omitting existing ones) unintentionally drops previous pets.
- Mixing numeric (unquoted) coverage values causes validation failures—always keep them strings.
- Forgetting to persist the users email and zip code.
Add a Pet to an Existing Quote
While the recommended approach for broad changes is to re-POST the full quote payload (see Updating an Existing Quote), you can alternatively append a single new pet using a dedicated endpoint:
POST /v2/quotes/fullquote/{quoteId}/pet
Endpoint reference: Add Pet Endpoint
Request Body
{
"name": "Zeus",
"breedId": 1,
"gender": "Male",
"age": "SixWeeksToTwelveMonths"
}
Response
Returns the same structure as a full quote response (including updated fullQuotePets, and premiumSummary).
When to Use This Endpoint
Use the pet endpoint when:
- You are only adding (not modifying or removing) a pet.
- Existing pets and coverage selections remain unchanged.
- You want to avoid reconstructing and resending the full quote payload.
Prefer a full quote re-POST when:
- Any existing pet details change (age, breedId, gender, name).
- A pet is removed.
- Coverage selections or payment frequency change.
- You need to add multiple pets in one operation (batch in full payload instead).
Backend Example (Minimal C#)
app.MapPost("/add-pet/{quoteId}", async (string quoteId, AddPetRequest pet, HttpClient http, IConfiguration cfg) =>
{
var upstream = new HttpRequestMessage(HttpMethod.Post, $"https://[embrace-test-endpoint]/v2/quotes/fullquote/{quoteId}/pet")
{
Content = new StringContent(JsonSerializer.Serialize(pet), Encoding.UTF8, "application/json")
};
upstream.Headers.Add("epi-apim-subscription-key", cfg["EMBRACE_API_KEY"]);
var resp = await http.SendAsync(upstream);
var body = await resp.Content.ReadAsStringAsync();
return Results.Content(body, "application/json", resp.StatusCode);
});
public record AddPetRequest(string Name, int BreedId, string Gender, string Age);
Considerations
- Validation errors (e.g., unsupported
agetoken) follow the same pattern as the full quote endpoint. - Premium impacts reflect the new aggregate pet set immediately in the returned response.
- If you subsequently need to change coverage or remove a pet, switch to the full re-POST strategy to avoid divergence.
- To modify an existing pet instead of adding one, see Update a Pet.
Update a Pet on an Existing Quote
Update a single existing pet (demographics and/or that pet’s coverage) without recreating or resending the full quote payload by calling:
PUT /v2/quotes/fullquote/{quoteId}/pet
Endpoint reference: Update Pet Endpoint
Use this when ONLY one pet’s attributes or coverage need to change and all other pets & global selections remain the same.
Request Body
{
"petId": "string",
"name": "Riley",
"ageInYears": "Three",
"gender": "Female",
"breedId": 452,
"reimbursementPercentage": "Ninety",
"annualWellnessReward": "None",
"annualDeductible": "FiveHundred",
"annualReimbursementLimit": "Unlimited",
"examFeeCoverage": true,
"drugDentalCoverage": false
}
Field notes:
petId(required): Must match an existing pet returned in prior quote responses (fullQuotePets[].id).ageInYears: Uses the same token set documented under Pet Age Values. When changing age buckets (e.g., birthday rollover) use the appropriate enum token.- Coverage fields (
reimbursementPercentage,annualWellnessReward,annualDeductible,annualReimbursementLimit,examFeeCoverage,drugDentalCoverage) represent the desired updated selection for THIS pet. - Omitted mutable fields are typically treated as unchanged (consult live schema for any truly required coverage fields on this endpoint).
Response
Returns the full quote response (same shape as a POST /v2/quotes/fullquote), reflecting the updated pet and recalculated premiums.
When to Use This Endpoint
Prefer this endpoint when:
- Only one pet’s demographics (name, gender, age, breed) change.
- Only one pet’s coverage selection changes.
- You need a lightweight call (smaller payload) versus reconstructing all pets.
Use a full quote re-POST instead when:
- Multiple pets are changing simultaneously.
- A pet is being removed.
- You are adding AND modifying in a single step (batch those changes via full re-POST for clarity).
- Global factors like payment frequency or multiple coverage combinations (
rateShopCoverageSelections) are being altered.
Backend Example (Minimal C#)
app.MapPut("/update-pet/{quoteId}", async (string quoteId, UpdatePetRequest pet, HttpClient http, IConfiguration cfg) =>
{
var upstream = new HttpRequestMessage(HttpMethod.Put, $"https://[embrace-test-endpoint]/v2/quotes/fullquote/{quoteId}/pet")
{
Content = new StringContent(JsonSerializer.Serialize(pet), Encoding.UTF8, "application/json")
};
upstream.Headers.Add("epi-apim-subscription-key", cfg["EMBRACE_API_KEY"]);
var resp = await http.SendAsync(upstream);
var body = await resp.Content.ReadAsStringAsync();
return Results.Content(body, "application/json", resp.StatusCode);
});
public record UpdatePetRequest(
string PetId,
string Name,
string AgeInYears,
string Gender,
int BreedId,
string ReimbursementPercentage,
string AnnualWellnessReward,
string AnnualDeductible,
string AnnualReimbursementLimit,
bool ExamFeeCoverage,
bool DrugDentalCoverage
);
Considerations
- Ensure the UI has already fetched and stored the
petIdfrom a prior response before allowing edits. - If you also need to add a pet in the same user action, skip this endpoint and re-POST the full quote with both changes applied.
- Coverage value forms (word or numeric string) follow the same rules documented in Rate Shopping Overview.
- Age token transitions (e.g., from
TwotoThree) should only be applied when appropriate; avoid auto-incrementing client-side. - For auditing, log both the previous and new coverage selections around the call.
Update Payment Frequency
Change only the payment frequency (Monthly ↔ Yearly) for an existing quote without resending pets, coverage selections, or analytics by calling:
PUT /v2/quotes/{quoteId}/contact
Endpoint reference: Update Contact / Payment Frequency
Request Body
{
"billingInformation": {
"paymentFrequency": "Monthly"
}
}
Only include the billingInformation.paymentFrequency you want applied. The service responds with the FULL quote response (same model as the original fullquote POST) with updated premium fields reflecting the new mode.
When to Use
- User toggles between Monthly and Yearly after the initial quote.
- No pet or coverage changes are occurring at the same time.
- You want a minimal payload vs. reconstructing and re-posting all pets.
Prefer Full Re-POST When
- Pets are added/removed/modified simultaneously.
- Coverage selections change.
- Multiple changes (pets + frequency + coverage) should be applied atomically.
Backend Example (Minimal C#)
app.MapPut("/update-frequency/{quoteId}", async (string quoteId, UpdateFrequencyRequest body, HttpClient http, IConfiguration cfg) =>
{
var upstream = new HttpRequestMessage(HttpMethod.Put, $"https://[embrace-test-endpoint]/v2/quotes/{quoteId}/contact")
{
Content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json")
};
upstream.Headers.Add("epi-apim-subscription-key", cfg["EMBRACE_API_KEY"]);
var resp = await http.SendAsync(upstream);
var json = await resp.Content.ReadAsStringAsync();
return Results.Content(json, "application/json", resp.StatusCode);
});
public record UpdateFrequencyRequest(BillingInformation BillingInformation);
public record BillingInformation(string PaymentFrequency);
Considerations
- Validate only allowed values (
Monthly,Yearly) before calling. - Premium-related fields (e.g.,
monthlyPremium,paymentDueRecurring) in the returnedpremiumSummaryreflect the new billing mode. - For responsive UX, optimistically update UI state while awaiting response, then reconcile if failure occurs.
- Log frequency change events separately from full re-quotes for analytics clarity.
Next Steps
- For redirect-based purchase: proceed to Display Quote Details and optionally update pets: /docs/qe-steps#step-2-display-quote-details-to-customer
- For Stripe purchase: proceed to Checkout: /docs/steps#step-2-make-a-request-to-the-checkout-endpoint
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.